/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <errno.h>

#include <uk/vma_types.h>
#include <uk/vmem.h>
#include <uk/test.h>
#include <uk/list.h>
#include <uk/print.h>
#include <tn/timer.h>
#include <tn/systick.h>
#include "internal_timer.h"
#include <uk/atomic.h>

#define pr_info(fmt, ...)      \
	_uk_printk(KLVL_INFO, __NULL, __NULL, 0x0, fmt, ##__VA_ARGS__)


int excution;

void timeout_func1(void *parameter)
{
	pr_info("timeout func1\n");
}

void timeout_func2(void *parameter)
{
	unsigned long timer_flag = ukplat_lcpu_save_irqf();

	excution++;
	ukplat_lcpu_restore_irqf(timer_flag);
}

void dump_timer(void)
{
	struct timer *node;
	struct uk_list_head *timer_list_head;

	timer_list_head = tn_timer_get_list();
	UK_ASSERT(timer_list_head != NULL);
	uk_list_for_each_entry(node, timer_list_head, list) {
		pr_info("node timeout is %x,flag is %d\n",
			node->timeout_tick, node->flag);
	};
}



UK_TESTCASE(tntimer, test_tn_timer_list_operations)
{
	systick_t t1 = 0x100000;
	systick_t t2 = 0x200000;
	systick_t t3 = 0x300000;
	systick_t t4 = 0x260000;
	systick_t t5 = 0x50000;

	struct timer *temp;

	struct timer *timer1 = tn_timer_create(
		t1, *timeout_func1, NULL, TN_TIMER_FLAG_PERIODIC);


	struct timer *timer2 = tn_timer_create(
		t2, *timeout_func1, NULL, TN_TIMER_FLAG_ONE_SHOT);
	struct timer *timer3 = tn_timer_create(
		t3, *timeout_func1, NULL, TN_TIMER_FLAG_ONE_SHOT);
	struct timer *timer4 = tn_timer_create(
		t4, *timeout_func1, NULL, TN_TIMER_FLAG_ONE_SHOT);
	struct timer *timer5 = tn_timer_create(
		t5, *timeout_func1, NULL, TN_TIMER_FLAG_ONE_SHOT);

	UK_TEST_EXPECT_NOT_NULL(timer1);
	UK_TEST_EXPECT_NOT_NULL(timer2);
	UK_TEST_EXPECT_NOT_NULL(timer3);
	UK_TEST_EXPECT_NOT_NULL(timer4);

	tn_timer_init(timer1, t1, *timeout_func1, NULL, TN_TIMER_FLAG_ONE_SHOT);
	UK_TEST_EXPECT(timer1->flag == TN_TIMER_FLAG_ONE_SHOT);

	tn_timer_start(timer1);
	tn_timer_start(timer2);
	tn_timer_start(timer3);
	tn_timer_start(timer4);
	tn_timer_start(timer5);

	struct timer *node;
	struct uk_list_head *timer_list_head = tn_timer_get_list();


	tn_timer_delete(timer2);
	timer2 = tn_timer_create(
		t2, *timeout_func1, NULL, TN_TIMER_FLAG_ONE_SHOT);
	tn_timer_start(timer2);

	systick_t last_tick = tn_timer_next_tick() + tn_systick_get_tick();

	temp = uk_list_first_entry_or_null(timer_list_head, struct timer, list);
	UK_TEST_EXPECT_NOT_NULL(temp);
	UK_TEST_EXPECT_SNUM_LT(temp->timeout_tick - last_tick, 20);
	tn_timer_delete(temp);

	temp = uk_list_first_entry_or_null(timer_list_head, struct timer, list);
	UK_TEST_EXPECT_NOT_NULL(temp);
	UK_TEST_EXPECT_SNUM_LT(temp->timeout_tick - last_tick-0xB0000,
	 20);
	tn_timer_delete(temp);

	temp = uk_list_first_entry_or_null(timer_list_head, struct timer, list);
	UK_TEST_EXPECT_NOT_NULL(temp);
	UK_TEST_EXPECT_SNUM_LT(temp->timeout_tick - last_tick - 0x1B0000,
	 20);
	tn_timer_delete(temp);


	temp = uk_list_first_entry_or_null(timer_list_head, struct timer, list);
	UK_TEST_EXPECT_NOT_NULL(temp);
	UK_TEST_EXPECT_SNUM_LT(temp->timeout_tick - last_tick - 0x210000,
	 20);
	tn_timer_delete(temp);

	temp = uk_list_first_entry_or_null(timer_list_head, struct timer, list);
	UK_TEST_EXPECT_NOT_NULL(temp);
	UK_TEST_EXPECT_SNUM_LT(temp->timeout_tick - last_tick - 0x2B0000,
	 20);
	tn_timer_delete(temp);

	UK_INIT_LIST_HEAD(tn_timer_get_list());

}



UK_TESTCASE(tntimer, test_tn_timer_announce)
{
	systick_t t1 = 100;
	systick_t t2 = 200;
	systick_t t3 = 200;
	systick_t t4 = 300;
	systick_t t5 = 302;

	struct timer *node;
	struct uk_list_head *timer_list_head = tn_timer_get_list();

	struct timer *timer1 = tn_timer_create(
		t1, *timeout_func2, NULL, TN_TIMER_FLAG_ONE_SHOT);
	struct timer *timer2 = tn_timer_create(
		t2, *timeout_func2, NULL, TN_TIMER_FLAG_ONE_SHOT);
	struct timer *timer3 = tn_timer_create(
		t3, *timeout_func2, NULL, TN_TIMER_FLAG_ONE_SHOT);
	struct timer *timer4 = tn_timer_create(
		t4, *timeout_func2, NULL, TN_TIMER_FLAG_ONE_SHOT);
	struct timer *timer5 = tn_timer_create(
		t5, *timeout_func2, NULL, TN_TIMER_FLAG_ONE_SHOT);

	UK_TEST_EXPECT_NOT_NULL(timer1);
	UK_TEST_EXPECT_NOT_NULL(timer2);
	UK_TEST_EXPECT_NOT_NULL(timer3);

	systick_t cur = tn_systick_get_tick();

	tn_timer_start(timer1);
	tn_timer_start(timer2);
	tn_timer_start(timer3);
	tn_timer_start(timer4);
	tn_timer_start(timer5);

	while (excution < 1) {
		asm volatile("" ::: "memory");
		/*
		 *do nothing,wait for timeout func
		 */
	}
	UK_TEST_EXPECT_SNUM_LT(tn_systick_get_tick() - cur - 100, 20);
	while (excution < 2) {
		asm volatile("" ::: "memory");
		/*
		 *do nothing,wait for timeout func
		 */
	}
	UK_TEST_EXPECT_SNUM_LT(tn_systick_get_tick() - cur - 200, 20);
	UK_TEST_EXPECT_SNUM_EQ(excution, 3);
	while (excution < 4) {
		asm volatile("" ::: "memory");
		/*
		 *do nothing,wait for timeout func
		 */
	}
	UK_TEST_EXPECT_SNUM_LT(tn_systick_get_tick() - cur - 300, 20);
	while (excution < 5) {
		asm volatile("" ::: "memory");
		/*
		 *do nothing,wait for timeout func
		 */
	}
	UK_TEST_EXPECT_SNUM_LT(tn_systick_get_tick() - cur - 302, 20);
	dump_timer();
	UK_TEST_EXPECT_SNUM_EQ(excution, 5);

	UK_INIT_LIST_HEAD(tn_timer_get_list());

}


uk_testsuite_register(tntimer, NULL);
